#ifndef __GLESRenderSystem_H__
#define __GLESRenderSystem_H__


#include "U2GLESPreRequest.h"
#include "U2RenderSystem.h"


U2EG_NAMESPACE_BEGIN


class U2GLESContext;
class U2GLESSupport;
class U2RenderWindow;
class U2HardwareBufferManager;
class U2Viewport;
class U2GLESRTTManager;


/** Implementation of GL as a rendering system.
 */
class _U2GLESShare GLESRenderSystem : public U2RenderSystem
{
public:
    // Default constructor / destructor
    GLESRenderSystem();
    virtual ~GLESRenderSystem();

    // ----------------------------------
    // Overridden U2RenderSystem functions
    // ----------------------------------

    /** @see U2RenderSystem. */
    const U2String& getName(void) const;

    /** @See U2RenderSystem. */
    U2ConfigOptionMap& getConfigOptions(void);

    /** @See U2RenderSystem. */
    void setConfigOption(const U2String &name, const U2String &value);

    /** @See U2RenderSystem. */
    U2String validateConfigOptions(void);

    /** @see U2RenderSystem. */
    virtual U2RenderSystemCapabilities* createRenderSystemCapabilities() const;

    /** @see U2RenderSystem. */
    void shutdown(void);

    /// @copydoc RenderSystem::_createRenderWindow
    U2RenderWindow* _createRenderWindow(const U2String &name, unsigned int width, unsigned int height, 
        bool fullScreen, const NameValuePairList *miscParams = 0);

    /** @see RenderSystem. */
    void initialiseFromRenderSystemCapabilities(U2RenderSystemCapabilities* caps, U2RenderTarget* primary);

    /** @see RenderSystem. */
    void _setViewport(U2Viewport *vp);

    /// @copydoc RenderSystem::_createDepthBufferFor
    U2DepthBuffer* _createDepthBufferFor( U2RenderTarget *renderTarget );

    /// Mimics D3D9RenderSystem::_getDepthStencilFormatFor, if no FBO RTT manager, outputs GL_NONE
    void _getDepthStencilFormatFor( GLenum internalColourFormat, GLenum *depthFormat,
        GLenum *stencilFormat );

    /** @see RenderSystem. */
    U2RenderWindow* _initialise(bool autoCreateWindow, const U2String& windowTitle = "U2 Render NativeWindowType");

    /** @See RenderSystem. */
    void _render(const U2RenderOperation& op);

    /** @See RenderSystem. */
    void _beginFrame(void);

    /** @See RenderSystem. */
    void _endFrame(void);

    /** @See RenderSystem. */
    void _setWorldMatrix(const U2Matrix4 &m);
    /** @See RenderSystem. */
    void _setViewMatrix(const U2Matrix4 &m);
    /** @See RenderSystem. */
    void _setProjectionMatrix(const U2Matrix4 &m);
    /** @See RenderSystem. */
    void _convertProjectionMatrix(const U2Matrix4& matrix,
        U2Matrix4& dest, bool forGpuProgram = false);
    void _makeOrthoMatrix(const Radian& fovy, u2real aspect, u2real nearPlane, u2real farPlane,
        U2Matrix4& dest, bool forGpuProgram = false);

    /** @See RenderSystem. */
    void _setTexture(size_t unit, bool enabled, const U2TexturePtr &tex);
    /** @See RenderSystem. */
    void _setTextureCoordSet(size_t stage, size_t index);

    /** @See RenderSystem. */
    void clearFrameBuffer(unsigned int buffers,
        const U2ColourValue& colour = U2ColourValue::Black,
        u2real depth = 1.0f/*, unsigned short stencil = 0*/);
    void _setDiscardBuffers(unsigned int flags) { mDiscardBuffers = flags; }
    unsigned int getDiscardBuffers(void) { return mDiscardBuffers; }

    /** @See RenderSystem. */
    void _setPolygonMode(PolygonMode level);

    /** @See RenderSystem. */
    void _setTextureUnitFiltering(size_t unit, FilterType ftype, FilterOptions filter);

    /** @See RenderSystem. */
    /**********************@@@@@@@@@@@@@@@@@@@@@
    void _setTextureLayerAnisotropy(size_t unit, unsigned int maxAnisotropy);
    */

    /** @See RenderSystem. */
    void _setTextureMipmapBias(size_t unit, float bias);

    /** @See RenderSystem. */
    void _setTextureBlendMode(size_t stage, const LayerBlendModeEx& bm);

    /** @See RenderSystem. */
    void _setTextureAddressingMode(size_t stage, const TextureUnitState::UVWAddressingMode& uvw);

    /** @See RenderSystem. */
    void _setTextureBorderColour(size_t stage, const U2ColourValue& colour);

    /** @See RenderSystem. */
    /**********************@@@@@@@@@@@@@@@@@@@@@
    void _setTextureCoordCalculation(size_t stage, TexCoordCalcMethod m,
        const U2Frustum* frustum = 0);
    */

    /** @See RenderSystem. */
    void _setTextureMatrix(size_t stage, const U2Matrix4& xform);





    // ----------------------------------
    // GLESRenderSystem specific members
    // ----------------------------------

    /** Returns the main context */
    U2GLESContext* _getMainContext() { return mMainContext; }

    /** Unregister a render target->context mapping. If the context of target 
        is the current context, change the context to the main context so it
        can be destroyed safely. 
    @note This is automatically called by the destructor of GLESContext.
    */
    void _unregisterContext(U2GLESContext *context);

    /** Switch GL context, dealing with involved internal cached states too
    */
    void _switchContext(U2GLESContext *context);

    void initialiseContext(U2RenderWindow* primary);

    /** One time initialization for the RenderState of a context. Things that
        only need to be set once, like the LightingModel can be defined here.
    */
    void _oneTimeContextInitialization();

    /** Set current render target to target, enabling its GL context if needed
     */
    void _setRenderTarget(U2RenderTarget *target);

    GLenum _getPolygonMode(void) { return mPolygonMode; }

    /// Internal method for anisotropy validation
    GLfloat _getCurrentAnisotropy(size_t unit);

protected:
    void setClipPlanesImpl(const U2PlaneList& planeList);

private:
    void makeGLMatrix(GLfloat gl_matrix[16], const U2Matrix4& m);
    bool activateGLTextureUnit(size_t unit);
    GLuint getCombinedMinMipFilter(void) const;
    GLint getTextureAddressingMode(TextureUnitState::TextureAddressingMode tam) const;

private:
    /// GL support class, used for creating windows etc.
    U2GLESSupport               *mGLSupport;
    /// The main GL context - main thread only.
    U2GLESContext               *mMainContext;
    /// The current GL context  - main thread only.
    U2GLESContext               *mCurrentContext;

    /// Store last colour write state
    bool                        mColourWrite[4];
    /// Store last depth write state
    GLboolean                   mDepthWrite;
    /// Store last stencil mask state
    u2uint32                    mStencilMask;

    /// Check if the GL system has already been initialised
    bool                        mGLInitialised;

    U2HardwareBufferManager*    mHardwareBufferManager;

    /** Manager object for creating render textures.
        Direct render to texture via GL_OES_framebuffer_object is preferable 
        to pbuffers, which depend on the GL support used and are generally 
        unwieldy and slow. However, FBO support for stencil buffers is poor.
    */
    U2GLESRTTManager            *mRTTManager;

    /// What texture coord set each texture unit is using
    size_t                      mTextureCoordIndex[U2_MAX_TEXTURE_LAYERS];

    /// Number of fixed-function texture units
    unsigned short              mFixedFunctionTextureUnits;

    /// View matrix to set world against
    U2Matrix4                   mViewMatrix;
    U2Matrix4                   mWorldMatrix;

    /** Used to store the number of mipmaps in the currently bound texture.  This is then
     used to modify the texture unit filtering.  Some GL ES implementations e.g. iPhone, 
     have a more strict implementation, if the current texture has no mipmaps and a filter that 
     requires them is requested, it is as if the texture is unbound.
     */
    size_t                      mTextureMipmapCount;

    /** These variables are used for caching RenderSystem state.
        They are cached because OpenGL state changes can be quite expensive,
        which is especially important on mobile or embedded systems.
     */
    u2ushort                    mActiveTextureUnit;

    /// Mask of buffers who contents can be discarded if GL_EXT_discard_framebuffer is supported
    unsigned int                mDiscardBuffers;
    /** OpenGL ES doesn't support setting the PolygonMode like desktop GL
        So we will cache the value and set it manually
     */
    GLenum                      mPolygonMode;

    /// Last min & mip filtering options, so we can combine them
    FilterOptions               mMinFilter;
    FilterOptions               mMipFilter;

    /**********************@@@@@@@@@@@@@@@@@@@@@
    GLfloat                     mAutoTextureMatrix[16];
    bool                        mUseAutoTextureMatrix;
    */
};

    
U2EG_NAMESPACE_END


#endif
